package com.cblz.bd.server.core.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cblz.bd.common.core.constant.BdAdminConstant;
import com.cblz.bd.common.core.constant.RedisConstant;
import com.cblz.bd.common.core.entity.BaAdminGroup;
import com.cblz.bd.common.core.entity.BaMenuRule;
import com.cblz.bd.common.core.entity.system.IndexSearchParams;
import com.cblz.bd.common.core.entity.system.MenuTree;
import com.cblz.bd.common.core.exception.BdAdminException;
import com.cblz.bd.common.core.utils.TreeUtil;
import com.cblz.bd.redis.spring.boot.starter.service.RedisService;
import com.cblz.bd.server.core.mapper.BaMenuRuleMapper;
import com.cblz.bd.server.core.service.BaAdminGroupService;
import com.cblz.bd.server.core.service.BaMenuRuleService;
import com.cblz.bd.server.utils.CacheCleanUtil;
import io.vavr.control.Option;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * @author yanjieping
 * @description 针对表【ba_menu_rule(菜单和权限规则表)】的数据库操作Service实现
 * @createDate 2022-11-04 15:09:53
 */
@Service("baMenuRuleService")
@RequiredArgsConstructor
public class BaMenuRuleServiceImpl extends ServiceImpl<BaMenuRuleMapper, BaMenuRule>
        implements BaMenuRuleService {

    private final BaAdminGroupService baAdminGroupService;
    private final RedisService redisService;

    private final CacheCleanUtil cacheCleanUtil;

    /**
     * 根据参数拿到对应的菜单信息
     *
     * @param indexSearchParams 查询参数，包含分页等信息
     * @return
     * @des 如果参数为空，那么就是查询所有菜单信息且不进行分页等操作
     */
    public List<MenuTree> getMenuTrees(IndexSearchParams indexSearchParams) {
        List<BaMenuRule> list;

        Map<Object, Object> menuAllCache = redisService.hmget(RedisConstant.MENU_ALL_PREFIX.getValue());

        if (Objects.isNull(indexSearchParams) && !Objects.isNull(menuAllCache) && !menuAllCache.isEmpty()) {
            return (List<MenuTree>) menuAllCache.get("menu");
        }

        /*
        查询indexSearchParams不等于空的数据
         */
        if (!Objects.isNull(indexSearchParams)
                && StringUtils.isNotBlank(indexSearchParams.getInitKey())
                && StringUtils.isNotBlank(indexSearchParams.getInitValue())) {
            Map<Object, Object> menuIndexSearchCache = redisService.hmget(RedisConstant.MENU_ID_CACHE_PREFIX.getValue() + "");

            if (!menuIndexSearchCache.isEmpty()) {
                return (List<MenuTree>) menuIndexSearchCache.get("menuSelectList");
            }

            if (indexSearchParams.getInitValues().isEmpty()) {
                list = this.list(new QueryWrapper<BaMenuRule>()
                        .eq(indexSearchParams.getInitKey(), indexSearchParams.getInitValue())
                        .lambda()
                        .orderByDesc(BaMenuRule::getWeigh));
            } else {
                list = this.list(new QueryWrapper<BaMenuRule>()
                        .in(indexSearchParams.getInitKey(), indexSearchParams.getInitValues())
                        .lambda()
                        .orderByDesc(BaMenuRule::getWeigh));
            }
        } else {
            list = this.list(new LambdaQueryWrapper<BaMenuRule>().orderByDesc(BaMenuRule::getWeigh));
        }

        // 如果数据库里没有数据，那么缓存一个10秒的空对象，防止缓存穿透
        if (list.isEmpty() && Objects.isNull(indexSearchParams)) {
            redisService.hmset(RedisConstant.MENU_ALL_PREFIX.getValue(), Map.of("menu", ""), 10L, TimeUnit.SECONDS);
            return Collections.emptyList();
        } else if (!Objects.isNull(indexSearchParams) && list.isEmpty()) {
            if (indexSearchParams.getInitValues().isEmpty()) {
                redisService.hmset(RedisConstant.MENU_ID_CACHE_PREFIX.getValue() + indexSearchParams.getInitValue(), Map.of("menuSelectList", ""), 10L, TimeUnit.SECONDS);
                return Collections.emptyList();
            }
            indexSearchParams.getInitValues().forEach((v) -> redisService.hmset(RedisConstant.MENU_ID_CACHE_PREFIX.getValue() + v, Map.of("menuSelectList", ""), 10L, TimeUnit.SECONDS));
            return Collections.emptyList();
        }

        List<MenuTree> trees = new ArrayList<>();
        buildTrees(trees, list);
        List<MenuTree> menuTrees = (List<MenuTree>) TreeUtil.build(trees);

        if (!menuTrees.isEmpty()) {
            // 如果是选择器需要的数据，那么将树形菜单转成List
            if (!Objects.isNull(indexSearchParams)
                    && Boolean.TRUE.equals(indexSearchParams.getSelect())) {
                List<MenuTree> selectList = new ArrayList<>();
                TreeUtil.recursionTreeToList(menuTrees, selectList);
                return Boolean.TRUE.equals(redisService.hmset(RedisConstant.MENU_ID_CACHE_PREFIX.getValue() + "all", Map.of("menuSelectList", selectList), 30L, TimeUnit.DAYS)) ? selectList : Collections.emptyList();
            }
            return Boolean.TRUE.equals(redisService.hmset(RedisConstant.MENU_ALL_PREFIX.getValue(), Map.of("menu", menuTrees), 30L, TimeUnit.DAYS)) ? menuTrees : Collections.emptyList();
        }
        return Collections.emptyList();
    }

    /**
     * @Author: kejiang
     * @Date: 2022/11/16 10:51
     * @Description:
     */
    @Override
    public void sortable(Integer id, Integer targetId) {
        BaMenuRule baMenuRuleId = baseMapper.selectById(id);
        List<BaMenuRule> baMenuRuleList = baseMapper.selectList(new QueryWrapper<BaMenuRule>().eq("pid", baMenuRuleId.getPid()));
        BaMenuRule baMenuRuleTargetId = baseMapper.selectById(targetId);
        if (baMenuRuleId.getWeigh() >= baMenuRuleTargetId.getWeigh()) {
            baMenuRuleId.setWeigh(baMenuRuleTargetId.getWeigh() - 1);
            for (BaMenuRule baMenuRule : baMenuRuleList) {
                if (baMenuRule.getWeigh() < baMenuRuleTargetId.getWeigh()) {
                    baMenuRule.setWeigh(baMenuRule.getWeigh() - 1);
                    baseMapper.updateById(baMenuRule);
                }
            }
        } else {
            baMenuRuleId.setWeigh(baMenuRuleTargetId.getWeigh() + 1);
            for (BaMenuRule baMenuRule : baMenuRuleList) {
                if (baMenuRule.getWeigh() > baMenuRuleTargetId.getWeigh()) {
                    baMenuRule.setWeigh(baMenuRule.getWeigh() + 1);
                    baseMapper.updateById(baMenuRule);
                }
            }
        }
        baseMapper.updateById(baMenuRuleId);

        cacheCleanUtil.cleanMenuCache(true, true, (String) null);
    }

    /**
     * 根据登录ID获取菜单信息
     *
     * @param loginId
     * @return
     */
    @Override
    public List<MenuTree> findMenuByLoginId(Integer loginId) {
        Map<Object, Object> cacheData = redisService.hmget(RedisConstant.MENU_PREFIX.getValue() + loginId);
        if (cacheData.isEmpty()) {
            String collect = Option.of(baAdminGroupService.getBdAdminRulesByLoginId(loginId)).getOrNull().stream().map(BaAdminGroup::getRules).collect(Collectors.joining(BdAdminConstant.SYSTEM_COMMA.getVal()));
            if (StringUtils.isNotBlank(collect)) {
                List<BaMenuRule> list;
                if (StringUtils.containsAny(collect, "*")) {
                    list = this.baseMapper.selectMap();
                } else {
                    list = this.list(new LambdaQueryWrapper<BaMenuRule>().in(BaMenuRule::getId, (Object) collect.split(BdAdminConstant.SYSTEM_COMMA.getVal())));
                }
                if (!list.isEmpty()) {
                    List<MenuTree> trees = new ArrayList<>();
                    buildTrees(trees, list);

                    List<MenuTree> menuTree = (List<MenuTree>) TreeUtil.build(trees);
                    // 缓存结果
                    if (Boolean.TRUE.equals(redisService.hmset(RedisConstant.MENU_PREFIX.getValue() + loginId, Map.of("menu", menuTree), 15L, TimeUnit.MINUTES))) {
                        return menuTree;
                    }
                }
            } else {
                // 如果数据是空的，就缓存一个空的对象
                redisService.hmset(RedisConstant.MENU_PREFIX.getValue() + loginId, Map.of("id", ""), 15L, TimeUnit.MINUTES);
            }
        } else {
            return (List<MenuTree>) cacheData.get("menu");
        }
        return null;
    }

    /**
     * 根据ID拿到菜单信息
     *
     * @param menuId
     * @return
     */
    @Override
    public BaMenuRule getInfoByMenuId(Integer menuId) {
        return Option.of(this.getOne(new LambdaQueryWrapper<BaMenuRule>().eq(BaMenuRule::getId, Option.some(menuId).getOrElseThrow(() -> new BdAdminException("查询的菜单ID不能为空"))))).getOrElseThrow(() -> new BdAdminException("找不到ID为：" + menuId + " 的菜单权限信息"));
    }

    private void buildTrees(List<MenuTree> trees, List<BaMenuRule> menus) {
        menus.forEach(menu -> trees.add(BeanUtil.copyProperties(menu, MenuTree.class)));
    }
}




